package com.atguigu.daijia.driver.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.driver.config.TencentCloudProperties;
import com.atguigu.daijia.driver.mapper.DriverFaceRecognitionMapper;
import com.atguigu.daijia.driver.mapper.DriverInfoMapper;
import com.atguigu.daijia.driver.mapper.DriverLoginLogMapper;
import com.atguigu.daijia.driver.mapper.DriverSetMapper;
import com.atguigu.daijia.driver.service.CosService;
import com.atguigu.daijia.driver.service.DriverAccountService;
import com.atguigu.daijia.driver.service.DriverInfoService;
import com.atguigu.daijia.model.entity.driver.*;
import com.atguigu.daijia.model.form.driver.DriverFaceModelForm;
import com.atguigu.daijia.model.form.driver.UpdateDriverAuthInfoForm;
import com.atguigu.daijia.model.vo.driver.DriverAuthInfoVo;
import com.atguigu.daijia.model.vo.driver.DriverInfoVo;
import com.atguigu.daijia.model.vo.driver.DriverLoginVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iai.v20200303.IaiClient;
import com.tencentcloudapi.iai.v20200303.models.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class DriverInfoServiceImpl extends ServiceImpl<DriverInfoMapper, DriverInfo> implements DriverInfoService {

    private final WxMaService wxMaService;
    private final DriverSetMapper driverSetMapper;
    private final DriverAccountService driverAccountService;
    private final DriverLoginLogMapper driverLoginLogMapper;
    private final CosService cosService;
    private final TencentCloudProperties tencentCloudProperties;
    private final DriverFaceRecognitionMapper driverFaceRecognitionMapper;

    /**
     * 司机端微信小程序授权登录
     * @param code
     * @return
     */
    @Override
    public Long login(String code) {
        // 调用微信 SDK 使用 code 换取 openId
        String openId = null;
        try {
            openId = wxMaService.getUserService().getSessionInfo(code).getOpenid();
        } catch (WxErrorException e) {
            e.printStackTrace();
            throw new GuiguException(ResultCodeEnum.WX_CODE_ERROR);
        }

        // 查询数据库中是否包含当前微信用户
        DriverInfo driverInfo = this.getOne(new LambdaQueryWrapper<DriverInfo>().eq(DriverInfo::getWxOpenId, openId));
        if (driverInfo == null) {
            // 当前微信用户未注册，新增
            driverInfo = new DriverInfo();
            driverInfo.setWxOpenId(openId);
            driverInfo.setNickname(String.valueOf(System.currentTimeMillis()));
            driverInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
            this.save(driverInfo);

            // 新注册司机初始化设置
            DriverSet driverSet = new DriverSet();
            driverSet.setDriverId(driverInfo.getId());
            // 订单里程，初始值为 0
            driverSet.setOrderDistance(BigDecimal.ZERO);
            // 接单里程，默认值为 5 公里
            driverSet.setAcceptDistance(new BigDecimal(SystemConstant.ACCEPT_DISTANCE));
            // 是否自动接单，0-不自动接单，1-自动接单，初始值为 0
            driverSet.setIsAutoAccept(0);
            // 服务状态，0-开始接单，1-停止接单，初始值为 0
            driverSet.setServiceStatus(0);
            driverSetMapper.insert(driverSet);

            // 新注册司机初始化账户
            DriverAccount driverAccount = new DriverAccount();
            driverAccount.setDriverId(driverInfo.getId());
            driverAccountService.save(driverAccount);
        }

        // 记录司机登录日志
        DriverLoginLog driverLoginLog = new DriverLoginLog();
        driverLoginLog.setDriverId(driverInfo.getId());
        driverLoginLog.setMsg("小程序登录");
        driverLoginLogMapper.insert(driverLoginLog);

        return driverInfo.getId();
    }

    /**
     * 获取登录司机的信息
     * @param driverId
     * @return
     */
    @Override
    public DriverLoginVo getDriverLoginInfo(Long driverId) {
        DriverInfo driverInfo = this.getById(driverId);
        DriverLoginVo driverLoginVo = new DriverLoginVo();
        BeanUtils.copyProperties(driverInfo, driverLoginVo);
        // 是否建档人脸识别
        Boolean isArchiveFace = StringUtils.hasText(driverInfo.getFaceModelId());
        driverLoginVo.setIsArchiveFace(isArchiveFace);
        return driverLoginVo;
    }

    /**
     * 获取司机认证信息
     * @param driverId
     * @return
     */
    @Override
    public DriverAuthInfoVo getDriverAuthInfo(Long driverId) {
        DriverInfo driverInfo = this.getById(driverId);
        DriverAuthInfoVo driverAuthInfoVo = new DriverAuthInfoVo();
        BeanUtils.copyProperties(driverInfo, driverAuthInfoVo);

        // 设置司机相关认证照片临时回显 URL
        driverAuthInfoVo.setIdcardFrontShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getIdcardFrontUrl()));
        driverAuthInfoVo.setIdcardBackShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getIdcardBackUrl()));
        driverAuthInfoVo.setIdcardHandShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getIdcardHandUrl()));
        driverAuthInfoVo.setDriverLicenseFrontShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getDriverLicenseFrontUrl()));
        driverAuthInfoVo.setDriverLicenseBackShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getDriverLicenseBackUrl()));
        driverAuthInfoVo.setDriverLicenseHandShowUrl(cosService.getTempImageUrl(driverAuthInfoVo.getDriverLicenseHandUrl()));

        return driverAuthInfoVo;
    }

    /**
     * 更新司机认证信息
     * @param updateDriverAuthInfoForm
     * @return
     */
    @Override
    public Boolean updateDriverAuthInfo(UpdateDriverAuthInfoForm updateDriverAuthInfoForm) {
        DriverInfo driverInfo = new DriverInfo();
        driverInfo.setId(updateDriverAuthInfoForm.getDriverId());
        BeanUtils.copyProperties(updateDriverAuthInfoForm, driverInfo);
        return this.updateById(driverInfo);
    }

    /**
     * 创建司机人脸模型
     * @param driverFaceModelForm
     * @return
     */
    @Override
    public Boolean createDriverFaceModel(DriverFaceModelForm driverFaceModelForm) {
        DriverInfo driverInfo = this.getById(driverFaceModelForm.getDriverId());
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey
            Credential credential = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个 http 选项，可选，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个 client 选项，可选，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的 client 对象，clientProfile 是可选的
            IaiClient client = new IaiClient(credential, tencentCloudProperties.getRegion(), clientProfile);
            // 实例化一个请求对象，每个接口都会对应一个 request 对象
            CreatePersonRequest req = new CreatePersonRequest();
            // 设置人脸识别的人员库 ID
            req.setGroupId(tencentCloudProperties.getPersonGroupId());
            // 设置人员基本信息
            req.setPersonId(driverInfo.getId().toString());
            req.setGender(Long.valueOf(driverInfo.getGender()));
            req.setQualityControl(4L);
            req.setUniquePersonControl(4L);
            req.setPersonName(driverInfo.getName());
            req.setImage(driverFaceModelForm.getImageBase64());

            CreatePersonResponse resp = client.CreatePerson(req);
            if (StringUtils.hasText(resp.getFaceId())) {
                // 司机的人脸模型创建成功，将模型 ID 保存到数据库
                driverInfo.setFaceModelId(resp.getFaceId());
                this.updateById(driverInfo);
            }
        } catch (TencentCloudSDKException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 获取司机设置信息
     * @param driverId
     * @return
     */
    @Override
    public DriverSet getDriverSet(Long driverId) {
        return driverSetMapper.selectOne(new LambdaQueryWrapper<DriverSet>().eq(DriverSet::getDriverId, driverId));
    }

    /**
     * 判断司机当日是否进行过人脸识别
     * @param driverId
     * @return
     */
    @Override
    public Boolean isFaceRecognition(Long driverId) {
        Long count = driverFaceRecognitionMapper.selectCount(new LambdaQueryWrapper<DriverFaceRecognition>()
                .eq(DriverFaceRecognition::getDriverId, driverId)
                .eq(DriverFaceRecognition::getFaceDate, LocalDate.now()));
        return count != 0;
    }

    /**
     * 司机人脸识别
     * https://cloud.tencent.com/document/product/867/44983
     * @param driverFaceModelForm
     * @return
     */
    @Override
    public Boolean verifyDriverFace(DriverFaceModelForm driverFaceModelForm) {
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个 http 选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个 client 选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的 client 对象，clientProfile 是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 实例化一个请求对象，每个接口都会对应一个 request 对象
            VerifyFaceRequest req = new VerifyFaceRequest();
            req.setImage(driverFaceModelForm.getImageBase64());
            req.setPersonId(driverFaceModelForm.getDriverId().toString());
            // 返回的 resp 是一个 VerifyFaceResponse 的实例，与请求对象对应
            VerifyFaceResponse resp = client.VerifyFace(req);
            if (resp.getIsMatch()) {
                // 司机照片比对成功，继续进行司机的人脸静态活体检测
                if (this.detectDriverLiveFace(driverFaceModelForm.getImageBase64())) {
                    // 静态活体检测通过，记录司机人脸识别信息
                    DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition();
                    driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId());
                    driverFaceRecognition.setFaceDate(new Date());
                    driverFaceRecognitionMapper.insert(driverFaceRecognition);
                    return true;
                }
            }
        } catch (TencentCloudSDKException e) {
            log.error(e.toString());
        }
        throw new GuiguException(ResultCodeEnum.DRIVER_FACE_VERIFY_FAILURE);
    }

    /**
     * 司机人脸静态活体检测
     * https://cloud.tencent.com/document/api/867/44985
     * @param imageBase64
     * @return
     */
    private boolean detectDriverLiveFace(String imageBase64) {
        try{
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(tencentCloudProperties.getSecretId(), tencentCloudProperties.getSecretKey());
            // 实例化一个 http 选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            // 实例化一个 client 选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的 client 对象，clientProfile 是可选的
            IaiClient client = new IaiClient(cred, tencentCloudProperties.getRegion(), clientProfile);
            // 实例化一个请求对象，每个接口都会对应一个 request 对象
            DetectLiveFaceRequest req = new DetectLiveFaceRequest();
            req.setImage(imageBase64);
            // 返回的 resp 是一个 DetectLiveFaceAccurateResponse 的实例，与请求对象对应
            DetectLiveFaceResponse resp = client.DetectLiveFace(req);
            if (resp.getIsLiveness()) {
                return true;
            }
        } catch (TencentCloudSDKException e) {
            log.error(e.toString());
        }
        throw new GuiguException(ResultCodeEnum.DRIVER_FACE_VERIFY_FAILURE);
    }

    /**
     * 更新司机接单状态
     * @param driverId
     * @param status
     * @return
     */
    @Override
    public Boolean updateServiceStatus(Long driverId, Integer status) {
        int update = driverSetMapper.update(new DriverSet(), new LambdaUpdateWrapper<DriverSet>().set(DriverSet::getServiceStatus, status).eq(DriverSet::getDriverId, driverId));
        return update > 0;
    }

    /**
     * 根据司机 ID 获取司机基本信息
     * @param driverId
     * @return
     */
    @Override
    public DriverInfoVo getDriverInfo(Long driverId) {
        DriverInfo driverInfo = this.getById(driverId);
        DriverInfoVo driverInfoVo = new DriverInfoVo();
        BeanUtils.copyProperties(driverInfo, driverInfoVo);
        // 计算司机驾龄
        Date driverLicenseIssueDate = driverInfo.getDriverLicenseIssueDate();
        int roundedYears = (int) Math.round((new Date().getTime() - driverLicenseIssueDate.getTime()) / (1000.0 * 60 * 60 * 24 * 365));
        driverInfoVo.setDriverLicenseAge(roundedYears);
        return driverInfoVo;
    }

    /**
     * 获取司机微信 OpenId
     * @param driverId
     * @return
     */
    @Override
    public String getDriverOpenId(Long driverId) {
        DriverInfo driverInfo = this.getOne(new LambdaQueryWrapper<DriverInfo>().eq(DriverInfo::getId, driverId).select(DriverInfo::getWxOpenId));
        return driverInfo.getWxOpenId();
    }
}